#
# Handles iterating through steps, storing objects for different step numbers, retrieving steps
# requires a matrix object to be passed to new() (or another object with the same methods)
#
# 2009, Mike Widner <mikewidner@gmail.com>
#
#######

package Fangcheng::Step;

use 5.006001;
use strict;
use warnings;
use vars qw( $AUTOLOAD @ISA );

use Carp;

require Exporter;
require Fangcheng::DB;

@ISA = qw( Fangcheng::DB Exporter );

my $VERSION = '0.01';

sub new {
	my ($class, $matrix) = @_;
	my $self = {};
	bless $self, ref($class) || $class;
	$self->step(0);
	$self->db( Fangcheng::DB->new() );
	if ( defined $matrix ) {
		$self->matrix( $matrix );	
	}
	return( $self );
}

# generates keys for saving matrices
sub _key {
	my ($self, $step) = @_;
	if (not defined $step) {
		croak "No step provided for key generation";
	}
	return( "_step$step" );
}

# saves matrix for given step; assumes current if none given
sub save {
	my ($self, $matrix, $step) = @_;
	if ( not defined $step ) {
		$step = $self->step();
	}
	$self->db->save( $self->_key( $step ), $matrix );
	if ( not defined $self->matrix() ) {
		$self->matrix( $matrix );
	}
}

# returns matrix for given step; if none given, assumes current step
sub get {
	my ($self, $step) = @_;
	if ( not defined $step ) {
		$step = $self->step();
	}
	my $matrix = $self->db->get( $self->_key( $step ) );

	# if no matrix, get the previous step, copy it, save it, return it
	if ( not defined $matrix and $step > 0 ) {
		$matrix = $self->get( $step - 1 );
		$matrix = $matrix->copy();
		$self->save( $matrix, $step );
	}
	elsif ( not defined $matrix ) {
		croak "No matrices found for any step!";	# avoid an infinite loop
	}
	return( $matrix );
}

# does not decrement step
sub prev {
	my ($self) = @_;
	my $prev = $self->get( $self->step() - 1 );
	if ( not defined $prev ) {
		croak "Failed getting previous matrix";
	}
	return( $prev );
}

# return matrix object for next step
# automatically increments step
sub next {
	my ($self) = @_;
	$self->step( $self->step + 1 );
	my $next = $self->get( $self->step );
	if ( not defined $next ) {
		# get() should ensure we don't reach this point
		croak "Failed getting next matrix";
	}

	return( $next );	
}

# returns the matrix for the highest numbered step
sub last {
	my ($self) = shift;
	my $last = $self->total();
	return( $self->db->get( $self->_key($last) ) );
}

# returns the matrix for the first step
# assumes we start at 0
sub first {
	my ($self) = shift;
	return( $self->db->get( $self->_key(0) ) );
}

# returns total steps available, useful for iterating since next() calls get(), which creates new steps
sub total {
	my ($self) = shift;
	my $last = 0;

	foreach my $step ( @{ $self->db->list() } ) {
		$step =~ s/[^\d]+//g;
		if ( $step > $last ) {
			$last = $step;
		}
	}
	return( $last );
}

1;